Маппинг на основе выражения доступа к свойству
Редактировал(а) Alexandr Fokin 2023/02/15 13:57
public class ExpressionMapper2<TEntity, TValue>
{
private readonly Dictionary<
string,
TValue
> Mapping;
private readonly Func<IEnumerable<Expression>, string> KeySelector;
private static readonly Func<IEnumerable<Expression>, string> DefaultKeySelector =
(e) =>
{
var expression = e.First(e2 => e2 is MemberExpression) as MemberExpression;
return expression.Member.Name;
};
public ExpressionMapper2(
IEnumerable<(string key, TValue value)> source,
Func<IEnumerable<Expression>, string>? keySelector = null
)
{
KeySelector = keySelector ?? DefaultKeySelector;
Mapping = source.ToDictionary(
e => e.key,
e => e.value
);
}
public ExpressionMapper2(
IEnumerable<(Expression<Func<TEntity, object>> keyExpression, TValue value)> source,
Func<IEnumerable<Expression>, string>? keySelector = null
)
{
KeySelector = keySelector ?? DefaultKeySelector;
Mapping = source.ToDictionary(
e => GetKey(e.keyExpression),
e => e.value
);
}
private string GetKey(Expression expression)
{
Visitor visitor = new Visitor();
visitor.Visit(expression);
var key = KeySelector(visitor.ExpressionList);
return key;
}
public bool TryGetValue<T>(
Expression<Func<TEntity, T>> expression,
out TValue value
)
{
var key = GetKey(expression);
return Mapping.TryGetValue(
key,
out value
);
}
public TValue GetValue<T>(
Expression<Func<TEntity, T>> expression
)
{
if (TryGetValue(expression, out var value))
{
return value;
}
else
{
throw new KeyNotFoundException();
}
}
// Можно сделать более эффективную и строгую реализацию
// для примера наиболее простая и общая
private sealed class Visitor
: ExpressionVisitor
{
public List<Expression> ExpressionList { get; private set; }
= new List<Expression>();
[return: NotNullIfNotNull("node")]
public override Expression? Visit(
Expression? node
)
{
ExpressionList.Add(node);
return base.Visit(node);
}
}
public static Builder CreateBuilder()
{
return new Builder();
}
public class Builder
{
private Func<IEnumerable<Expression>, string> KeySelector = DefaultKeySelector;
private List<(Expression, TValue)> Rules = new List<(Expression, TValue)>();
public Builder SetKeySelector(
Func<IEnumerable<Expression>, string> keySelector
)
{
KeySelector = keySelector;
return this;
}
public Builder AddRule<T>(
Expression<Func<TEntity, T>> rule,
TValue value
)
{
Rules.Add((rule, value));
return this;
}
public ExpressionMapper2<TEntity, TValue> Build()
{
(string key, TValue value)[] rules = new (string key, TValue value)[Rules.Count];
{
Visitor visitor = new Visitor();
int i = 0;
foreach (var elem in Rules)
{
visitor.Visit(elem.Item1);
var key = KeySelector(visitor.ExpressionList);
visitor.ExpressionList.Clear();
rules[i] = (key, elem.Item2);
i++;
}
}
return new ExpressionMapper2<TEntity, TValue>(
rules,
KeySelector
);
}
}
}
{
private readonly Dictionary<
string,
TValue
> Mapping;
private readonly Func<IEnumerable<Expression>, string> KeySelector;
private static readonly Func<IEnumerable<Expression>, string> DefaultKeySelector =
(e) =>
{
var expression = e.First(e2 => e2 is MemberExpression) as MemberExpression;
return expression.Member.Name;
};
public ExpressionMapper2(
IEnumerable<(string key, TValue value)> source,
Func<IEnumerable<Expression>, string>? keySelector = null
)
{
KeySelector = keySelector ?? DefaultKeySelector;
Mapping = source.ToDictionary(
e => e.key,
e => e.value
);
}
public ExpressionMapper2(
IEnumerable<(Expression<Func<TEntity, object>> keyExpression, TValue value)> source,
Func<IEnumerable<Expression>, string>? keySelector = null
)
{
KeySelector = keySelector ?? DefaultKeySelector;
Mapping = source.ToDictionary(
e => GetKey(e.keyExpression),
e => e.value
);
}
private string GetKey(Expression expression)
{
Visitor visitor = new Visitor();
visitor.Visit(expression);
var key = KeySelector(visitor.ExpressionList);
return key;
}
public bool TryGetValue<T>(
Expression<Func<TEntity, T>> expression,
out TValue value
)
{
var key = GetKey(expression);
return Mapping.TryGetValue(
key,
out value
);
}
public TValue GetValue<T>(
Expression<Func<TEntity, T>> expression
)
{
if (TryGetValue(expression, out var value))
{
return value;
}
else
{
throw new KeyNotFoundException();
}
}
// Можно сделать более эффективную и строгую реализацию
// для примера наиболее простая и общая
private sealed class Visitor
: ExpressionVisitor
{
public List<Expression> ExpressionList { get; private set; }
= new List<Expression>();
[return: NotNullIfNotNull("node")]
public override Expression? Visit(
Expression? node
)
{
ExpressionList.Add(node);
return base.Visit(node);
}
}
public static Builder CreateBuilder()
{
return new Builder();
}
public class Builder
{
private Func<IEnumerable<Expression>, string> KeySelector = DefaultKeySelector;
private List<(Expression, TValue)> Rules = new List<(Expression, TValue)>();
public Builder SetKeySelector(
Func<IEnumerable<Expression>, string> keySelector
)
{
KeySelector = keySelector;
return this;
}
public Builder AddRule<T>(
Expression<Func<TEntity, T>> rule,
TValue value
)
{
Rules.Add((rule, value));
return this;
}
public ExpressionMapper2<TEntity, TValue> Build()
{
(string key, TValue value)[] rules = new (string key, TValue value)[Rules.Count];
{
Visitor visitor = new Visitor();
int i = 0;
foreach (var elem in Rules)
{
visitor.Visit(elem.Item1);
var key = KeySelector(visitor.ExpressionList);
visitor.ExpressionList.Clear();
rules[i] = (key, elem.Item2);
i++;
}
}
return new ExpressionMapper2<TEntity, TValue>(
rules,
KeySelector
);
}
}
}